package cs342.uwosh.edu.ContactsApp;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import android.database.sqlite.SQLiteDatabase;

public class DatabaseThread<T> implements Callable<T>, Runnable{
	static long MAX_WAIT = 4; //seconds
	
	Object[] args;
	SQLiteDatabase database;
	
	public DatabaseThread(SQLiteDatabase database, Object... args) {
		this.database = database;
		this.args = args;
	}
	
	// To be implemented on instantiation
	public T call() throws Exception {
		// TODO Auto-generated method stub
		return null;
	}
	
	// To be implemented on instantiation
	public void run() {
		// TODO Auto-generated method stub
		
	}
	/** Call Method	
	 * 
	 *	 Use this method to send a query call to the database. It will then return whatever data you needed.
	 *
	 *		@param databaseThread - the object that contains the query
	 *
	 * Example:
	 * 		In this example I created a new Class that implements Callable that takes one argument.
	 * 		So that when call() is implemented it can use inner-method variables from my previous thread.
	 * 
	 * 		call<Contact>(new DatabaseThread(database, PRIVATE_VAL0, PRIVATE_VAL1) {
	 *		        public Object call() {
	 *					long insertId = database.insert(ContactsDatabaseHelper.TABLE_CONTACTS, null,
	 *							(ContentValues)  args[0]);
	 *					// args[0] -> PRIVATE_VAL0
	 *					Cursor cursor = database.query(ContactsDatabaseHelper.TABLE_CONTACTS,
	 *							allColumns, ContactsDatabaseHelper.COL_ID + " = " + args[1], null,
	 *							null, null, null);
	 *					// args[1] -> PRIVATE_VAL1
	 *					cursor.moveToFirst();
	 *					return cursorToContact(cursor);
	 *		        }
	 *	        });
	 */
	
	@SuppressWarnings("unchecked")
	static <T> T call(Callable<T> databaseThread) {
		T obj = null;
		final ExecutorService service;
        final Future<T> task;
        
        service = Executors.newFixedThreadPool(1);        
        task    = service.submit(databaseThread);
        		
		try {
        	int time = 0;
        	while(!task.isDone() && !task.isCancelled()) {
        		Thread.sleep(10);
        		time++;
        		// if more than 2 seconds then cancel
        		if(time > (MAX_WAIT * 10)) {
        			task.cancel(true);
        			throw new InterruptedException("Database did not finish query in time");
        		}
        	}
            // A
        	if(time <= (MAX_WAIT * 10)) {
        		obj = task.get();
    		}
        } catch(final InterruptedException ex) {
            ex.printStackTrace();
        } catch(final ExecutionException ex) {
            ex.printStackTrace();
        }

        service.shutdownNow();
		return obj;
		
	}
	
	/** Run
	 * 	Will wait for the thread to finish
	 * 
	 * @param run - the runnable object that contains the db query
	 */
	
	static void run(DatabaseThread databaseThread) {
		run(true, databaseThread);
	}
	
	static void run(boolean wait, DatabaseThread databaseThread) {
		Thread thread = new Thread(databaseThread);
		thread.start();
		if(wait) {
			try {
				thread.join(MAX_WAIT * 100);
				if(thread.isAlive()) {
					thread.interrupt();
					throw new InterruptedException("Database did not finish query in time");
				}
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	static void setMaxWait(long maxWait) {
		MAX_WAIT = maxWait;
	}

}
